/*
 *downloading.hh
*/

#include "downloading.hh"

using namespace std;
/*
 * downloader thread handler
 * 
 * @param param - input param structure
 *
 */

void* Downloader::thread_handler(void* param){
   param_t* temp=(param_t*)param;
   Downloader* obj=temp->obj;
   free(temp);
   char * downloadContainer_;
   char * downloadFilehead;
   char * downloadblockcphead;
   int retSize;
   int index=0;
   downloadFilehead=(char *)malloc(sizeof(char)*20);
   downloadblockcphead=(char *)malloc(sizeof(char)*300);
    init_t signal;
    obj->signalBuffer_->Extract(&signal);
    char* filename = signal.filename;
    printf("thread:filename is %s\n",filename);
    /*send the hf into server*/
    obj->socket_->initDownload(filename,32);

   /*download mate data into container*/
   obj->socket_->downloadChunk(downloadFilehead,&retSize);
  
  /*get the file header*/
  fileMDHead_t* header=(fileMDHead_t*)downloadFilehead;
  index=sizeof(fileMDHead_t);
  int filesize=header->filesize;
  int left=header->left;
  int numofblock=header->numOfblock;
  printf("the filesize is %d,the left is %d\n",filesize,left);
  downloadContainer_=(char *)malloc(sizeof(char)*filesize);

   Item_t input;
   input.type=FILE_HEAD;
   memcpy(&(input.fileObj),header,sizeof(fileMDHead_t));
  /* add the header object into ringbuffer */
    obj->ringBuffer_->Insert(&input,sizeof(input));
   
   //upload the metabuffer
    if(index == retSize){
         obj->socket_->downloadChunk(downloadblockcphead,&retSize);
         printf("download the file size is %d\n",retSize);
         index = 0;
     }
    
    //upload the buffer
    obj->socket_->downloadChunk(downloadContainer_,&retSize);
    printf("download the file size is %d\n",retSize);

    int count=0;
    int count_mate=0;
     for(int i=0;i<numofblock;i++) {
     blockCpMDHead_t* blockcphead=(blockCpMDHead_t*)(downloadblockcphead+count_mate);
     count_mate+=sizeof(blockCpMDHead_t);
     int blockid=blockcphead->blockcpid;
     int blokcsize=blockcphead->blockcpSize;
     printf("the blocksize is %d %d\n",blokcsize,blockid);
     
     Item_t input;
     memcpy(&(input.blockcpObj.blockcp_header),blockcphead,sizeof(blockCpMDHead_t));
     input.blockcpObj.data=(unsigned char *)malloc(sizeof(unsigned char)*blokcsize);
     memcpy(input.blockcpObj.data,downloadContainer_+count,blokcsize);
     count+=blokcsize;
     obj->ringBuffer_->Insert(&input,sizeof(input));
    }

   return NULL;
}


Downloader::Downloader(int userID,Decoder* obj){

 //create/open the kyey-value database
  dbOptions_.create_if_missing=true;
  //modify the sst file
  dbOptions_.write_buffer_size=MEM_TABLE_SIZE;
  //modify the cache ???
  dbOptions_.block_cache=leveldb::NewLRUCache(BLOCK_CACHE_SIZE);
  dbOptions_.filter_policy=leveldb::NewBloomFilterPolicy(BLOOM_FILTER_KEY_BITS);
  leveldb::Status openStat=leveldb::DB::Open(dbOptions_,"./meta/DedupDB",&db_);  
  if(openStat.ok()==false){
    fprintf(stderr,"Error:fail to open/create the database %s \n",dbDirName_.c_str());
    fprintf(stderr, "Status: %s \n", openStat.ToString().c_str());
		exit(1);
  }
   decoderObj_=obj;
   ringBuffer_ = new RingBuffer<Item_t>(DOWNLOAD_RB_SIZE, true, 1);
   blocpMdHeadBuffer_=new RingBuffer<blockCpMDHead_t>(DOWNLOAD_RB_SIZE, true, 1);;
   signalBuffer_ = new RingBuffer<init_t>(DOWNLOAD_RB_SIZE, true, 1);
   /* create threads */
   param_t* param = (param_t*)malloc(sizeof(param_t));      // thread's parameter
   param->obj = this;
   pthread_create(&tid_,0,&thread_handler, (void*)param);

  FILE* fin=fopen("./config","rb");
  char line[255];
  char ch[2]=":";
  int res=fscanf(fin,"%s",line);
  if(res==0)
  printf("read line failure!\n");
  char *token=strtok(line,ch);
  char *ip=token;
  token=strtok(NULL,ch);
  int port=atoi(token);
  socket_=new Socket(ip,port,userID);
  fclose(fin);
}

Downloader::~Downloader(){
  delete db_;
  delete dbOptions_.block_cache;
  delete dbOptions_.filter_policy;
  delete socket_;
  delete ringBuffer_;
}

int Downloader::downloadFile(char *fname){
//get the file hf with file name in leveldb
  leveldb::Slice *keySlice=new leveldb::Slice(fname,strlen(fname));
  std::string valueString;
  char *hf;
  hf=(char*)malloc(sizeof(char)*32);
  leveldb::Status status=db_->Get(readOptions_,*keySlice,&valueString);
   if(status.ok()==false) {
   printf(" the hf has not storaged\n");
     return 0;
  }else {
  // strcpy(hf,valueString.c_str());
   printf("the hf is %s\n",valueString.c_str());
  }
   /* add init object for download */
    init_t input;
    input.filename = (char*)(valueString.c_str());
    input.namesize =32;
    signalBuffer_->Insert(&input, sizeof(init_t));

   //get the file head
   Item_t headerObj;
   ringBuffer_->Extract(&headerObj);

   fileMDHead_t* header=&(headerObj.fileObj);
   int numOfblock=header->numOfblock;
   decoderObj_->setTotal(numOfblock);
   int filesize=header->filesize;
   int left=header->left;
   printf("download file::filesize %d %d\n",filesize,numOfblock);

   int count=0;
   while(count<numOfblock){
	   Item_t output;
	   int blocksize;
	   Decoder::BlockCp_t blockcp;
	   ringBuffer_->Extract(&output);
	   blockCpMDHead_t* temp=&(output.blockcpObj.blockcp_header);
	   blocksize=temp->blockcpSize;
           blockcp.data=(unsigned char*)malloc(sizeof(unsigned char)*blocksize);
	   memcpy(blockcp.key,temp->key,32);
           if(count<numOfblock-1){
             blockcp.left=0;
           }else
           {
            blockcp.left=left+1;
           }
           blockcp.blockcpSize=blocksize;
         memcpy(blockcp.data,output.blockcpObj.data,blocksize);
         decoderObj_->add(&blockcp);
         count++;
   }
  free(hf);
  delete keySlice;
  printf("downloading a file functions\n");
 return 1;
}


/*
 * test if it's the end of downloading a file
 *
 */
int Downloader::indicateEnd(){  
        /* trying to join all threads */
        pthread_join(tid_,NULL);
    return 1;
}




